﻿using System;
using Lang = SerialManager.Localization.EpisodeLoader;

namespace SerialManager.Loaders
{
    /// <summary>
    /// Basic implementation of IEpisodeLoader
    /// </summary>
    public abstract class EpisodeLoader : IEpisodeLoader
    {
        /// <summary>
        /// Logger instance for logging purposes
        /// </summary>
        protected readonly log4net.ILog Logger;

        /// <summary>
        /// Generic method for creating loader
        /// </summary>
        /// <typeparam name="T">Type derived from EpisodeLoader</typeparam>
        /// <param name="serialName">Name of serial, that will be loaded</param>
        /// <param name="autoStart">Loader will be automatically started upon creation</param>
        /// <returns>Created loader</returns>
        public static IEpisodeLoader Create<T>( string serialName, bool autoStart ) where T : EpisodeLoader, new()
        {
            var l = new T { SerialName = serialName };
            if( autoStart )
                l.Start();
            return l;
        }

        /// <summary>
        /// Parameter-less constructor for basic initialization
        /// </summary>
        protected EpisodeLoader()
        { 
            Logger = log4net.LogManager.GetLogger( this.GetType() );
            Episodes = new System.Collections.Generic.List<EpisodeInfo>(); 
        }

        /// <summary>
        /// Current status of loader
        /// </summary>
        public EpisodeLoaderStatus Status { get; protected set; }

        /// <summary>
        /// Reason of aborting work
        /// </summary>
        protected EpisodeLoaderAbortReason AbortReason { get; set; }

        /// <summary>
        /// Gets name of serial used for loading
        /// Needs to be set in constructor
        /// </summary>
        public string SerialName { get; protected set; }

        /// <summary>
        /// Number of seasons, that were loaded
        /// </summary>
        public int SeasonCount { get; protected set; }

        /// <summary>
        /// Informations about episodes, that were loaded
        /// </summary>
        public System.Collections.Generic.List<EpisodeInfo> Episodes { get; private set; }

        /// <summary>
        /// Indicates that request was aborted by user
        /// </summary>
        protected bool Aborted { get; set; }

        /// <summary>
        /// Starts helper thread with calling method <see cref="MakeProgress"/>
        /// </summary>
        public virtual void Start()
        {
            var initThread = new System.Threading.Thread( MakeProgress );
            initThread.IsBackground = true;
            initThread.Priority = System.Threading.ThreadPriority.Lowest;
            initThread.Start();
        }

        /// <summary>
        /// Helper thread method for displaying progress until <see cref="Finished"/>
        /// Writes simple dot (.) to console every second
        /// Also checks, if loader isn't working too long <see cref="Config.EpisodeLoaderTimeout"/> and aborts it in that case
        /// </summary>
        protected virtual void MakeProgress()
        {
            DateTime timeout = DateTime.Now.Add(Config.EpisodeLoaderTimeout);
            while( !Finished )
            {
                if( DateTime.Now > timeout )
                {
                    Abort(EpisodeLoaderAbortReason.Timeout);
                    return;
                }

                Console.Write( "." );
                System.Threading.Thread.Sleep( 1000 );
            }
        }

        /// <summary>
        /// Aborting of work
        /// Sets <see cref="Status"/> to <see cref="EpisodeLoaderStatus.Aborted"/>
        /// Writes message depending od reason of aborting to console
        /// Sets <see cref="Aborted"/> flag to check in derived classes
        /// Sets <see cref="Finished"/> to indicate, that work is done
        /// </summary>
        /// <param name="reason"></param>
        public virtual void Abort(EpisodeLoaderAbortReason reason)
        {
            Aborted = true;
            Logger.DebugFormat( "Abort requested, reason: {0}", reason );
            //Console.WriteLine( " "+Lang.Aborted );
            Status = EpisodeLoaderStatus.Aborted;
            AbortReason = reason;
            Finished = true;
        }

        /// <summary>
        /// Indiciates that work is done (even unsuccessful)
        /// </summary>
        public bool Finished { get; protected set; }

        /// <summary>
        /// Error handling method
        /// Writes message <see cref="Lang.Error"/> to console
        /// Sets <see cref="Status"/> to <see cref="EpisodeLoaderStatus.DoneWithError"/>
        /// Sets <see cref="Finished"/> to indicate, that work is done
        /// </summary>
        /// <remarks>Doesn't writes error to Logger, do it with your own message describing position where error occured</remarks>
        /// <param name="ex">Exception, that was raised</param>
        protected virtual void OnError( Exception ex )
        {
            Console.WriteLine( " "+Lang.Error );
            Status = EpisodeLoaderStatus.DoneWithError;
            Finished = true;
        }
    }
}
